#pragma once
#include "DXContext.h"
#include "primitives.h"
#include "Image.h"
#include "enums.h"

namespace DXBase
{
	namespace D2D
	{
		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class NativeBrush sealed
		{
		internal:
			Microsoft::WRL::ComPtr<ID2D1Brush> brush;
		};

		// http://msdn.microsoft.com/EN-US/library/dd756651(v=VS.85,d=hv.2).aspx
		[Windows::Foundation::Metadata::WebHostHidden]
		public interface class IBrush : IContextBound
		{
		public:
			NativeBrush^ GetNativeBrush();
			property float Opacity;
			property float3x2 Transform;
		};

		// http://msdn.microsoft.com/EN-US/library/dd372207(v=VS.85,d=hv.2).aspx
		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class SolidColorBrush sealed : IBrush
		{
			ContextWatcher watcher;

			NativeBrush^ nbrush;
			Microsoft::WRL::ComPtr<ID2D1SolidColorBrush> pbrush;

			Windows::UI::Color color;
			float opacity;
			float3x2 transform;

		internal:
			virtual NativeBrush^ GetNativeBrush() = IBrush::GetNativeBrush;

			virtual void ClearDXData() = IContextBound::ClearDXData 
			{
				nbrush = nullptr; 
				pbrush = nullptr;
			}
			virtual void LoadDXData() = IContextBound::LoadDXData { GetNativeBrush(); }
			virtual property DXContext^ Context
			{
				DXContext^ get() = IContextBound::Context::get { return watcher.Context; } 
				void set(DXContext^ ctxt) = IContextBound::Context::set { watcher.Context = ctxt; }
			}

		public:
			SolidColorBrush();

			virtual property float Opacity
			{
				float get() { return opacity; }
				void set(float value)
				{
					opacity = value < 0 ? 0 : value > 1 ? 1 : value;
					if (pbrush)
						pbrush->SetOpacity(opacity);
				}
			}
			virtual property float3x2 Transform
			{
				float3x2 get() { return transform; }
				void set(float3x2 value)
				{
					transform = value;
					if (pbrush)
						pbrush->SetTransform(makeD2D(transform));
				}
			}

			property Windows::UI::Color Color 
			{ 
				Windows::UI::Color get() { return color; }
				void set(Windows::UI::Color value)
				{
					color = value;
					ClearDXData();
				}
			};
		};

		public value struct GradientStop
		{
		public:
			float position;
			Windows::UI::Color color;
		};

		// http://msdn.microsoft.com/EN-US/library/dd371488(v=VS.85,d=hv.2).aspx
		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class LinearGradientBrush sealed : IBrush
		{
			ContextWatcher watcher;

			NativeBrush^ nbrush;
			Microsoft::WRL::ComPtr<ID2D1LinearGradientBrush> pbrush;

			float opacity;
			float3x2 transform;
			float2 startPoint, endPoint;
			D2D::ColorInterpolationMode gamma;
			GradientSpreadMethod emode;
			Windows::Foundation::Collections::IIterable<GradientStop>^ gradientStops;

		internal:
			virtual NativeBrush^ GetNativeBrush() = IBrush::GetNativeBrush;

			virtual void ClearDXData() = IContextBound::ClearDXData 
			{
				nbrush = nullptr; 
				pbrush = nullptr;
			}
			virtual void LoadDXData() = IContextBound::LoadDXData { GetNativeBrush(); }
			virtual property DXContext^ Context
			{
				DXContext^ get() = IContextBound::Context::get { return watcher.Context; } 
				void set(DXContext^ ctxt) = IContextBound::Context::set { watcher.Context = ctxt; }
			}

		public:
			LinearGradientBrush();

			virtual property float Opacity
			{
				float get() { return opacity; }
				void set(float value)
				{
					opacity = value < 0 ? 0 : value > 1 ? 1 : value;
					if (pbrush)
						pbrush->SetOpacity(opacity);
				}
			}
			virtual property float3x2 Transform
			{
				float3x2 get() { return transform; }
				void set(float3x2 value)
				{
					transform = value;
					if (pbrush)
						pbrush->SetTransform(makeD2D(transform));
				}
			}
			property float2 StartPoint
			{
				float2 get() { return startPoint; }
				void set(float2 value)
				{
					startPoint = value;
					if (pbrush)
						pbrush->SetStartPoint(makeD2D(startPoint));
				}
			}
			property float2 EndPoint
			{
				float2 get() { return endPoint; }
				void set(float2 value)
				{
					endPoint = value;
					if (pbrush)
						pbrush->SetEndPoint(makeD2D(endPoint));
				}
			}
			property ColorInterpolationMode ColorInterpolationMode
			{
				D2D::ColorInterpolationMode get() { return gamma; }
				void set(D2D::ColorInterpolationMode value)
				{
					if (value == gamma)
						return;
					gamma = value;
					ClearDXData();
				}
			}
			// not using the Windows::UI::Xaml::Media enum as its values doesn't match Direct2D
			property GradientSpreadMethod SpreadMethod 
			{
				GradientSpreadMethod get() { return emode; }
				void set(GradientSpreadMethod value)
				{
					if (value == emode)
						return;
					emode = value;
					ClearDXData();
				}
			}
			property Windows::Foundation::Collections::IIterable<GradientStop>^ GradientStops
			{
				Windows::Foundation::Collections::IIterable<GradientStop>^ get() { return gradientStops; }
				void set(Windows::Foundation::Collections::IIterable<GradientStop>^ value)
				{
					if (value == gradientStops)
						return;
					gradientStops = value;
					ClearDXData();
				}
			}
		};


		// http://msdn.microsoft.com/EN-US/library/dd371529(v=VS.85,d=hv.2).aspx
		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class RadialGradientBrush sealed : IBrush
		{
			ContextWatcher watcher;

			NativeBrush^ nbrush;
			Microsoft::WRL::ComPtr<ID2D1RadialGradientBrush> pbrush;

			float opacity;
			float3x2 transform;
			float2 center, offset;
			Windows::Foundation::Size radius;
			D2D::ColorInterpolationMode gamma;
			GradientSpreadMethod emode;
			Windows::Foundation::Collections::IIterable<GradientStop>^ gradientStops;

		internal:
			virtual NativeBrush^ GetNativeBrush() = IBrush::GetNativeBrush;

			virtual void ClearDXData() = IContextBound::ClearDXData 
			{
				nbrush = nullptr; 
				pbrush = nullptr;
			}
			virtual void LoadDXData() = IContextBound::LoadDXData { GetNativeBrush(); }
			virtual property DXContext^ Context
			{
				DXContext^ get() = IContextBound::Context::get { return watcher.Context; } 
				void set(DXContext^ ctxt) = IContextBound::Context::set { watcher.Context = ctxt; }
			}

		public:
			RadialGradientBrush();

			virtual property float Opacity
			{
				float get() { return opacity; }
				void set(float value)
				{
					opacity = value < 0 ? 0 : value > 1 ? 1 : value;
					if (pbrush)
						pbrush->SetOpacity(opacity);
				}
			}
			virtual property float3x2 Transform
			{
				float3x2 get() { return transform; }
				void set(float3x2 value)
				{
					transform = value;
					if (pbrush)
						pbrush->SetTransform(makeD2D(transform));
				}
			}
			property float2 Center
			{
				float2 get() { return center; }
				void set(float2 value)
				{
					center = value;
					if (pbrush)
						pbrush->SetCenter(makeD2D(center));
				}
			}
			property float2 Offset
			{
				float2 get() { return offset; }
				void set(float2 value)
				{
					offset = value;
					if (pbrush)
						pbrush->SetGradientOriginOffset(makeD2D(offset));
				}
			}
			property Windows::Foundation::Size Radius
			{
				Windows::Foundation::Size get() { return radius; }
				void set(Windows::Foundation::Size value)
				{
					radius = value;
					if (pbrush)
					{
						pbrush->SetRadiusX(radius.Width);
						pbrush->SetRadiusY(radius.Height);
					}
				}
			}
			property D2D::ColorInterpolationMode ColorInterpolationMode
			{
				D2D::ColorInterpolationMode get() { return gamma; }
				void set(D2D::ColorInterpolationMode value)
				{
					if (value == gamma)
						return;
					gamma = value;
					ClearDXData();
				}
			}
			// not using the Windows::UI::Xaml::Media enum as its values doesn't match Direct2D
			property GradientSpreadMethod SpreadMethod 
			{
				GradientSpreadMethod get() { return emode; }
				void set(GradientSpreadMethod value)
				{
					if (value == emode)
						return;
					emode = value;
					ClearDXData();
				}
			}
			property Windows::Foundation::Collections::IIterable<GradientStop>^ GradientStops
			{
				Windows::Foundation::Collections::IIterable<GradientStop>^ get() { return gradientStops; }
				void set(Windows::Foundation::Collections::IIterable<GradientStop>^ value)
				{
					if (value == gradientStops)
						return;
					gradientStops = value;
					ClearDXData();
				}
			}
		};

		// http://msdn.microsoft.com/EN-US/library/dd371122(v=VS.85,d=hv.2).aspx
		[Windows::Foundation::Metadata::WebHostHidden]
		public ref class BitmapBrush sealed : IBrush
		{
			ContextWatcher watcher;

			NativeBrush^ nbrush;
			Microsoft::WRL::ComPtr<ID2D1BitmapBrush> pbrush;

			float opacity;
			float3x2 transform;
			D2D::Bitmap^ bitmap;
			GradientSpreadMethod spreadX, spreadY;
			BITMAP_INTERPOLATION_MODE bmpIMode;

		internal:
			virtual NativeBrush^ GetNativeBrush() = IBrush::GetNativeBrush;

			virtual void ClearDXData() = IContextBound::ClearDXData 
			{
				nbrush = nullptr; 
				pbrush = nullptr;
			}
			virtual void LoadDXData() = IContextBound::LoadDXData { GetNativeBrush(); }
			virtual property DXContext^ Context
			{
				DXContext^ get() = IContextBound::Context::get { return watcher.Context; } 
				void set(DXContext^ ctxt) = IContextBound::Context::set { watcher.Context = ctxt; }
			}

		public:
			BitmapBrush();

			virtual property float Opacity
			{
				float get() { return opacity; }
				void set(float value)
				{
					opacity = value < 0 ? 0 : value > 1 ? 1 : value;
					if (pbrush)
						pbrush->SetOpacity(opacity);
				}
			}
			virtual property float3x2 Transform
			{
				float3x2 get() { return transform; }
				void set(float3x2 value)
				{
					transform = value;
					if (pbrush)
						pbrush->SetTransform(makeD2D(transform));
				}
			}
			property D2D::Bitmap^ Bitmap
			{
				D2D::Bitmap^ get() { return bitmap; }
				void set(D2D::Bitmap^ value)
				{
					if (value == bitmap)
						return;
					bitmap = value;
					ClearDXData();
				}
			}
			property GradientSpreadMethod SpreadMethodX 
			{
				GradientSpreadMethod get() { return spreadX; }
				void set(GradientSpreadMethod value)
				{
					if (value == spreadX)
						return;
					spreadX = value;
					if (pbrush)
						pbrush->SetExtendModeX(static_cast<D2D1_EXTEND_MODE>(value));
				}
			}
			property GradientSpreadMethod SpreadMethodY
			{
				GradientSpreadMethod get() { return spreadY; }
				void set(GradientSpreadMethod value)
				{
					if (value == spreadY)
						return;
					spreadY = value;
					if (pbrush)
						pbrush->SetExtendModeY(static_cast<D2D1_EXTEND_MODE>(value));
				}
			}
			property BITMAP_INTERPOLATION_MODE InterpolationMode
			{
				BITMAP_INTERPOLATION_MODE get() { return bmpIMode; }
				void set(BITMAP_INTERPOLATION_MODE value)
				{
					if (value == bmpIMode)
						return;
					bmpIMode = value;
					if (pbrush)
						pbrush->SetInterpolationMode(static_cast<D2D1_BITMAP_INTERPOLATION_MODE>(value));
				}
			}
		};
	}
}